﻿#region Using

using System;
using System.Linq;
using System.Threading.Tasks;
using Vk.Methods.Common;
using Vk.Methods.Groups;
using Vk.Methods.Users;

#endregion

namespace Vk.Methods.Likes
{
    /// <summary>
    ///     Лайки
    /// </summary>
    public class Likes
    {
        private readonly Api _api;

        public Likes(Api api)
        {
            _api = api;
        }

        /// <summary>
        ///     Добавляет указанный объект в список Мне нравится текущего пользователя.
        /// </summary>
        /// <param name="type">
        ///     Идентификатор типа Like-объекта. Подробнее об идентификаторах объектов можно узнать на странице
        ///     Список типов Like-объектов.
        /// </param>
        /// <param name="itemId">Идентификатор Like-объекта.</param>
        /// <param name="accessKey">Ключ доступа в случае работы с приватными объектами</param>
        /// <param name="ownerId">
        ///     Идентификатор владельца Like-объекта. Если параметр не задан, то считается, что он равен идентифкатору текущего
        ///     пользователя.
        ///     В случае записей и комментариев на стене owner_id равен идентификатору страницы со стеной, а не автору записи.
        /// </param>
        /// <returns>
        ///     В случае успеха возвращает объект с полем likes, в котором находится текущее количество пользователей, которые
        ///     добавили данный объект в свой список Мне нравится.
        /// </returns>
        public async Task<int> Add(LikeType type, int itemId, int? ownerId = null, string accessKey = null)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            var query = new ApiQuery("likes.add")
                .Add("type", type.Value)
                .Add("item_id", itemId)
                .Add("owner_id", ownerId)
                .Add("access_key", accessKey);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Int("likes").HasValue ? result.Int("likes").Value : -1;
        }

        /// <summary>
        ///     Удаляет указанный объект из списка Мне нравится текущего пользователя.
        /// </summary>
        /// <param name="type">
        ///     Идентификатор типа Like-объекта. Подробнее об идентификаторах объектов можно узнать на странице
        ///     Список типов Like-объектов.
        /// </param>
        /// <param name="itemId">Идентификатор Like-объекта.</param>
        /// <param name="ownerId">
        ///     Идентификатор владельца Like-объекта. Если параметр не задан, то считается, что он равен идентифкатору текущего
        ///     пользователя.
        ///     В случае записей и комментариев на стене owner_id равен идентификатору страницы со стеной, а не автору записи.
        /// </param>
        /// <returns>
        ///     В случае успеха возвращает объект с полем likes, в котором находится текущее количество пользователей, которые
        ///     добавили данный объект в свой список Мне нравится.
        /// </returns>
        public async Task<int> Delete(LikeType type, int itemId, int? ownerId = null)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            var query = new ApiQuery("likes.delete")
                .Add("type", type.Value)
                .Add("item_id", itemId)
                .Add("owner_id", ownerId);


            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Int("likes").HasValue ? result.Int("likes").Value : -1;
        }

        /// <summary>
        ///     Получает список идентификаторов пользователей, которые добавили заданный объект в свой список Мне нравится.
        /// </summary>
        /// <param name="type">Тип Like-объекта. Подробнее о типах объектов можно узнать на странице Список типов Like-объектов.</param>
        /// <param name="ownerId">
        ///     Идентификатор владельца Like-объекта (id пользователя или id приложения). Если параметр type
        ///     равен sitepage, то в качестве owner_id необходимо передавать id приложения. Если параметр не задан, то считается,
        ///     что он равен либо идентификатору текущего пользователя, либо идентификатору текущего приложения (если type равен
        ///     sitepage).
        /// </param>
        /// <param name="itemId">
        ///     Идентификатор Like-объекта. Если type равен sitepage, то параметр item_id может содержать значение
        ///     параметра page_id, используемый при инициализации виджета «Мне нравится».
        /// </param>
        /// <param name="pageUrl">URL страницы, на которой установлен виджет «Мне нравится». Используется вместо параметра item_id.</param>
        /// <param name="filter">
        ///     Указывает, следует ли вернуть всех пользователей, добавивших объект в список "Мне нравится" или
        ///     только тех, которые рассказали о нем друзьям.
        /// </param>
        /// <param name="friendsOnly">
        ///     Указывает, необходимо ли возвращать только пользователей, которые являются друзьями текущего
        ///     пользователя.
        /// </param>
        /// <param name="offset">
        ///     Смещение, относительно начала списка, для выборки определенного подмножества. Если параметр не
        ///     задан, то считается, что он равен 0.
        /// </param>
        /// <param name="count">
        ///     Количество возвращаемых идентификаторов пользователей.
        ///     Если параметр не задан, то считается, что он равен 100, если не задан параметр friends_only, в противном случае 10.
        ///     Максимальное значение параметра 1000, если не задан параметр friends_only, в противном случае 100.
        /// </param>
        /// <param name="skipOwn">Не возвращать самого пользователя. </param>
        /// <returns>
        ///     В случае успеха возвращает объект со следующими полями:
        ///     count – общее количество пользователей, которые добавили заданный объект в свой список Мне нравится.
        ///     users – список индентификаторов пользователей с учетом параметров offset и count, которые добавили заданный объект
        ///     в свой список Мне нравится.
        /// </returns>
        public async Task<ListCount<int>> GetList(LikeType type,
                                                  int? ownerId = null,
                                                  int? itemId = null,
                                                  string pageUrl = null,
                                                  LikesFilter filter = null,
                                                  bool? friendsOnly = null,
                                                  int? offset = null,
                                                  int? count = null,
                                                  bool? skipOwn = null)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            var query = new ApiQuery("likes.getList", true)
                .Add("type", type.Value)
                .Add("owner_id", ownerId)
                .Add("page_url", pageUrl)
                .Add("item_id", itemId)
                .Add("filter", filter)
                .Add("friends_only", friendsOnly)
                .Add("count", count)
                .Add("skip_own", skipOwn)
                .Add("offset", offset);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed
                       ? new ListCount<int>(result.Int("count").Value,
                                            result.SelectNodes("items/*").Select(x => x.Int().Value).ToList())
                       : null;
        }

        /// <summary>
        ///     Получает список идентификаторов пользователей, которые добавили заданный объект в свой список Мне нравится.
        /// </summary>
        /// <param name="type">Тип Like-объекта.</param>
        /// <param name="ownerId">
        ///     Идентификатор владельца Like-объекта (id пользователя или id приложения). Если параметр type
        ///     равен sitepage, то в качестве owner_id необходимо передавать id приложения. Если параметр не задан, то считается,
        ///     что он равен либо идентификатору текущего пользователя, либо идентификатору текущего приложения (если type равен
        ///     sitepage).
        /// </param>
        /// <param name="itemId">
        ///     Идентификатор Like-объекта. Если type равен sitepage, то параметр item_id может содержать значение
        ///     параметра page_id, используемый при инициализации виджета «Мне нравится».
        /// </param>
        /// <param name="pageUrl">URL страницы, на которой установлен виджет «Мне нравится». Используется вместо параметра item_id.</param>
        /// <param name="filter">
        ///     Указывает, следует ли вернуть всех пользователей, добавивших объект в список "Мне нравится" или
        ///     только тех, которые рассказали о нем друзьям.
        /// </param>
        /// <param name="friendsOnly">
        ///     Указывает, необходимо ли возвращать только пользователей, которые являются друзьями текущего
        ///     пользователя.
        /// </param>
        /// <param name="offset">
        ///     Смещение, относительно начала списка, для выборки определенного подмножества. Если параметр не
        ///     задан, то считается, что он равен 0.
        /// </param>
        /// <param name="count">
        ///     Количество возвращаемых идентификаторов пользователей.
        ///     Если параметр не задан, то считается, что он равен 100, если не задан параметр friends_only, в противном случае 10.
        ///     Максимальное значение параметра 1000, если не задан параметр friends_only, в противном случае 100.
        /// </param>
        /// <param name="skipOwn">Не возвращать самого пользователя. </param>
        /// <returns>
        ///     Список, содержащий расширенную информацию о пользователях или сообществах.
        /// </returns>
        /// <see cref="User" />
        /// <see cref="Group" />
        public async Task<ListCount<object>> GetListExtended(LikeType type,
                                                             int? ownerId = null,
                                                             int? itemId = null,
                                                             string pageUrl = null,
                                                             LikesFilter filter = null,
                                                             bool? friendsOnly = null,
                                                             int? offset = null,
                                                             int? count = null,
                                                             bool? skipOwn = null)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            var query = new ApiQuery("likes.getList", true)
                .Add("type", type.Value)
                .Add("owner_id", ownerId)
                .Add("page_url", pageUrl)
                .Add("item_id", itemId)
                .Add("filter", filter)
                .Add("friends_only", friendsOnly)
                .Add("count", count)
                .Add("skip_own", skipOwn)
                .Add("extended", true)
                .Add("offset", offset);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed
                       ? new ListCount<object>(result.Int("count").Value,
                                               result.SelectNodes("items/*").Select<ApiNode, object>(x =>
                                               {
                                                   switch (x.String("type"))
                                                   {
                                                       case "profile":
                                                           return new User(x);
                                                       //TODO: check whether "page" is right name
                                                       case "page":
                                                           return new Group(x);
                                                       default:
                                                           throw new ArgumentOutOfRangeException();
                                                   }
                                               }).ToList())
                       : null;
        }

        /// <summary>
        ///     Проверяет находится ли объект в списке Мне нравится заданного пользователя.
        /// </summary>
        /// <param name="type">
        ///     Идентификатор типа Like-объекта. Подробнее об идентификаторах объектов можно узнать на странице
        ///     Список типов Like-объектов.
        /// </param>
        /// <param name="itemId">Идентификатор Like-объекта.</param>
        /// <param name="ownerId">
        ///     Идентификатор владельца Like-объекта. Если параметр не задан, то считается, что он равен
        ///     идентификатору текущего пользователя.
        /// </param>
        /// <param name="userId">
        ///     Идентификатор пользователя у которого необходимо проверить наличие объекта в списке Мне нравится.
        ///     Если параметр не задан, то считается, что он равен идентификатору текущего пользователя.
        /// </param>
        /// <returns>
        ///     В случае успеха возвращает одно из следующих числовых значений:
        ///     false – указанный Like-объект не входит в список Мне нравится пользователя с идентификатором user_id.
        ///     true – указанный Like-объект находится в списке Мне нравится пользователя с идентификатором user_id.
        /// </returns>
        public async Task<bool> IsLiked(LikeType type, int itemId, int? ownerId = null, int? userId = null)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            var query = new ApiQuery("likes.isLiked")
                .Add("type", type.Value)
                .Add("item_id", itemId)
                .Add("owner_id", ownerId)
                .Add("user_id", userId);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }
    }
}